package com.javaxiaobear.base.framework.jackson;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.javaxiaobear.base.common.utils.Dates;
import com.javaxiaobear.base.common.utils.JsonUtil;
import com.javaxiaobear.base.common.utils.SmartDateFormat;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.util.Locale;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

/**
 * jackson配置
 *
 * @author LarryYan
 * @date 2018 /7/3 11:48
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ObjectMapper.class, Jackson2ObjectMapperBuilder.class})
@AutoConfigureBefore(JacksonAutoConfiguration.class)
public class JacksonAutoConfigurer {

  @Bean("jacksonDateFormat")
  @ConditionalOnMissingBean
  public DateFormat dateFormat() {
    return new SmartDateFormat(Dates.DATETIME_FORMAT);
  }

  /**
   * 解决序列化和反序列化时，参数转换问题 addSerializer：序列化 （Controller 返回 给前端的json） date -> string
   *
   * <p>addDeserializer: 反序列化 （前端调用接口时，传递到后台的json） 枚举类型: {"code": "xxx"} -> Enum
   *
   * @param builder the builder
   * @return object mapper
   */
  @Bean
  @ConditionalOnClass(ObjectMapper.class)
  @ConditionalOnMissingBean
  public ObjectMapper jacksonObjectMapper(
      Jackson2ObjectMapperBuilder builder, @Qualifier("jacksonDateFormat") DateFormat dateFormat) {
    ObjectMapper objectMapper =
        builder
            .createXmlMapper(false)
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .timeZone(Dates.DEFAULT_TIMEZONE)
            .build();

    objectMapper
        .setLocale(Locale.CHINA)
        .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
        // 忽略未知字段
        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
        .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
        // 该特性决定parser是否允许JSON字符串包含非引号控制字符（值小于32的ASCII字符，包含制表符和换行符）。
        // 如果该属性关闭，则如果遇到这些字符，则会抛出异常。JSON标准说明书要求所有控制符必须使用引号，因此这是一个非标准的特性
        //                .configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true)
        .configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true)

        // 忽略不能转移的字符
        //                .configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER,
        // true)
        .configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true)

        // 单引号处理
        .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true)
        // 日期格式
        .setDateFormat(dateFormat);

    SimpleModule simpleModule =
        new SimpleModule()
            .addSerializer(Long.class, LongSerializer.instance)
            .addSerializer(Long.TYPE, LongSerializer.instance)
            .addSerializer(BigInteger.class, BigIntegerSerializer.instance)
            .addSerializer(BigDecimal.class, BigDecimalSerializer.instance);
    objectMapper.registerModule(simpleModule);

    builder.configure(objectMapper);
    // 初始化JSON工具
    JsonUtil.init(objectMapper);

    return objectMapper;
  }

  /**
   * serializerByType 解决json中返回的 LocalDateTime 格式问题 deserializerByType 解决string类型入参转为 LocalDateTime
   * 格式问题
   *
   * @return jackson 2 object mapper builder customizer
   */
  @Bean
  @ConditionalOnMissingBean(DefaultJackson2ObjectMapperBuilderCustomizer.class)
  public DefaultJackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    return new DefaultJackson2ObjectMapperBuilderCustomizer();
  }
}
